package personal.scanner;

import exceptions.IllegalDecimalException;
import exceptions.IllegalIdentifierException;
import exceptions.IllegalSymbolException;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class SimpleScanner implements MyScanner {
    private static class Lookahead {
        int get() {
            return input.charAt(ptr);
        }

        boolean haveNext() {
            return this.ptr < this.input.length();
        }

        void update() {
            ++ptr;
        }

        private int ptr = 0;

        private final String input;

        public Lookahead(String input) {
            this.input = input;
        }
    }


    /**
     * 扫描 input buffer，返回 token list
     *
     * @param input input buffer
     * @return token list
     */
    @Override
    public List<Token> scan(String input) throws IllegalSymbolException, IllegalDecimalException, IllegalIdentifierException {
        // 初始化数据结构
        final Stack<Integer> acceptBuffer = new Stack<>(); // 接受节点栈
        final StringBuilder inputBuffer = new StringBuilder(); // 输入栈
        final List<Token> tokens = new ArrayList<>(); // token 列表
        Lookahead lookahead = new Lookahead(input.toLowerCase());
        int u = DFA.startId(); // 当前节点
        while (lookahead.haveNext()) {
            if (DFA.isAcceptable(u)) acceptBuffer.add(u); // 如果当前节点是接受节点，那么放入 buffer
            // 开始处理
            if (lookahead.get() == ' ') { // 表达式发生显示分隔
                this.longestMatchHandle(acceptBuffer, inputBuffer, tokens); // 可能发生最长匹配
                u = DFA.startId(); // 回到开始节点
                lookahead.update(); // 跳转到下一个字符
                continue;
            }
            if (this.isLongestMatch(u, lookahead.get())) { // 发生最长匹配
                this.longestMatchHandle(acceptBuffer, inputBuffer, tokens); // 处理栈顶
                u = DFA.startId(); // 回到开始节点
                continue;
            } else if (this.isIllegalTransit(u, lookahead.get())) { // 出现非法转移
                this.exit(u, lookahead.get()); // 抛出异常
                throw new UnknownError();
            }
            // 只是普通的转移
            inputBuffer.append((char) lookahead.get()); // 将 lookahead 放入 buffer
            u = DFA.transitTo(u, lookahead.get()); // 跳转到下一个字符
            lookahead.update(); // 读取指针后移
        }
        if (DFA.isAcceptable(u)) { // 读取结束时，也可能发生最长匹配
            acceptBuffer.add(u);
            this.longestMatchHandle(acceptBuffer, inputBuffer, tokens);
        }
        return tokens;

    }


    /**
     * 判断是否是非法转移，
     *
     * @param u         当前节点
     * @param lookahead 下一位
     * @return 是否是非法转移
     */
    private boolean isIllegalTransit(int u, int lookahead) {
        if (!DFA.isInTransitions(lookahead)) return true;
        try {
            return DFA.transitTo(u, lookahead) == 0; // 目标转移节点
        } catch (NullPointerException ex) {
            return true;
        }
    }

    /**
     * 判断是否是最长匹配：当前节点是接受节点，且下一次转移是非法的
     *
     * @param u         当前节点
     * @param lookahead 下一位
     * @return 是否是最长匹配
     */
    private boolean isLongestMatch(int u, int lookahead) {
        return DFA.isAcceptable(u) && this.isIllegalTransit(u, lookahead);
    }

    /**
     * 处理函数，此时发生最长匹配，
     * 放入新的 token，内容是 input buffer，类型是 accept buffer 栈顶对应的枚举量。
     * 最后清空 input buffer 和 accept buffer
     *
     * @param acceptBuffer 接受节点栈
     * @param inputBuffer  输入栈
     * @param tokens       token 列表
     */
    private void longestMatchHandle(final Stack<Integer> acceptBuffer, final StringBuilder inputBuffer, final List<Token> tokens) {
        if (acceptBuffer.isEmpty()) return;
        // 取出并清空
        Class type = DFA.getAcceptType(acceptBuffer.peek()); // 类型
        String expression = inputBuffer.toString(); // 表达式
        acceptBuffer.clear(); // 清空 accept buffer
        inputBuffer.setLength(0); // 清空 input buffer
        // 处理
        if (type.equals(DecimalToken.class)) { // 数字
            tokens.add(new DecimalToken(expression));
            return;
        }
        if (type.equals(BooleanToken.class)) { // 布尔数
            tokens.add(new BooleanToken(expression));
            return;
        }
        if (type.equals(CompareOperatorToken.class)) { // 比较运算符
            tokens.add(new CompareOperatorToken(expression));
            return;
        }
        if (type.equals(LogicalOperatorToken.class)) { // 逻辑运算符
            if (expression.equals("!")) { // 单目
                tokens.add(new LogicalOperator1DToken(expression));
            } else {
                tokens.add(new LogicalOperator2DToken(expression));
            }
            return;
        }
        if (type.equals(DelimiterOperatorToken.class)) { // 分隔运算符
            tokens.add(new DelimiterOperatorToken(expression));
            return;
        }
        if (type.equals(FunctionToken.class)) { // 函数
            tokens.add(new FunctionToken(expression));
            return;
        }
        if (!type.equals(ArithmeticOperatorToken.class)) { // 不是算术运算符
            throw new UnknownError("节点的接受类型是未知的");
        }
        // 现在处理算术运算符
        if (expression.equals("?") || expression.equals(":")) { // 是三目
            tokens.add(new ArithmeticOperator3DToken(expression));
            return;
        }
        // 现在不是双目就是单目
        if (!expression.equals("-")) { // 不是 -，即是其它运算符，那么是双目
            tokens.add(new ArithmeticOperator2DToken(expression));
            return;
        }
        // 现在特判 -
        if (tokens.isEmpty()) { // 如果是开头，那么是单目
            tokens.add(new ArithmeticOperator1DToken(expression));
            return;
        }
        // 取出左侧，进行特判
        Token lastToken = tokens.get(tokens.size() - 1);
        if (lastToken instanceof OperandToken || // 左侧是运算数，那么是双目
                (lastToken instanceof DelimiterOperatorToken && lastToken.getExpression().equals(")")))  // 左侧是分隔符，且是 )，即是完整的表达式，那么是双目
        {
            tokens.add(new ArithmeticOperator2DToken(expression));
            return;
        }
        // 否则一律是单目
        tokens.add(new ArithmeticOperator1DToken(expression));

    }

    /**
     * 退出函数，负责抛异常
     *
     * @param u         出现异常时，指针所在的节点编号
     * @param lookahead 下一位
     */
    private void exit(int u, int lookahead) throws IllegalSymbolException, IllegalDecimalException, IllegalIdentifierException {
        if (u == 1 && Character.isLetter(lookahead)) { // 如果在开头识别到了非法字母，那么是非法标识符
            throw new IllegalIdentifierException();
        }
        if (!DFA.isInTransitions(lookahead)) { // 如果 lookahead 不在转移表中，那么是非法字符
            throw new IllegalSymbolException();
        }
        Class throwType = DFA.defaultException(u);
        if (throwType == IllegalSymbolException.class) {
            throw new IllegalSymbolException();
        } else if (throwType == IllegalDecimalException.class) {
            throw new IllegalDecimalException();
        } else if (throwType == IllegalIdentifierException.class) {
            throw new IllegalIdentifierException();
        } else {
            throw new UnknownError("词法分析时，出现未定义的异常");
        }
    }


    /**
     * 自动机
     */
    private final ScannerDFA DFA;


    /**
     * 构造函数
     */
    public SimpleScanner(ScannerDFA DFA) {
        this.DFA = DFA;
    }
}



